package controller

import (
	"encoding/json"
	"fmt"
	"github.com/alimoeeny/gooauth2"
	"github.com/cihub/seelog"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"strconv"

	"io/ioutil"
	"net/http"
	"qiniupkg.com/x/errors.v7"
	"wangchunguang_blog/model"
	"wangchunguang_blog/service"
	"wangchunguang_blog/system"
	"wangchunguang_blog/utils"
)

// git用户信息
type GitHubUserInfo struct {
	// 头像地址
	AvatarUrl string `json:"avatar_url"`
	// 个人简历
	Bio interface{} `json:"bio"`
	// 博客
	Blog string `json:"blog"`
	// 企业
	Company interface{} `json:"company"`
	// 创建时间
	CreatedAt string `json:"created_at"`
	//  邮箱
	Email interface{} `json:"email"`
	// 社交url
	EventsUrl string `json:"events_url"`

	Followers int `json:"followers"`

	FollowersUrl string `json:"followers_url"`

	Following int `json:"following"`

	FollowingUrl string `json:"following_url"`
	// 网址
	GistsUrl string `json:"gists_url"`
	// 唯一标识
	GravatarID string `json:"gravatar_id"`

	Hireable interface{} `json:"hireable"`
	// hrml 地址
	HTMLURL string `json:"html_url"`
	// ID
	ID int `json:"id"`
	// 地址
	Location interface{} `json:"location"`
	// 登录
	Login string `json:"login"`
	// 名称
	Name string `json:"name"`
	// 机构网址
	OrganizationsURL string `json:"organizations_url"`
	// 公众要点
	PublicGists int `json:"public_gists"`
	// 回购
	PublicRepos int `json:"public_repos"`
	// 收到事件网址
	ReceivedEventsURL string `json:"received_events_url"`
	// 回购网址
	ReposURL string `json:"repos_url"`
	// 网站管理员
	SiteAdmin bool `json:"site_admin"`
	// 星标网址
	StarredURL string `json:"starred_url"`
	// 订阅网址
	SubscriptionURL string `json:"subscription_url"`
	// 类型
	Type string `json:"type"`
	// 更新时间
	UpdatedAt string `json:"updated_at"`
	// 地址
	URL string `json:"url"`
}

// 获取登录
func SigninGet(c *gin.Context) {
	c.HTML(http.StatusOK, "auth/signin.html", nil)
}

// 获取注册
func SignupGet(c *gin.Context) {
	c.HTML(http.StatusOK, "auth/signup.html", nil)
}

// 退出
func LogoutGet(c *gin.Context) {
	// 获取session会话
	session := sessions.Default(c)
	session.Clear()
	session.Save()
	// 重定向
	c.Redirect(http.StatusSeeOther, "/signin")
}

// 注册
func SignupPost(c *gin.Context) {
	var (
		err error
		res = gin.H{}
	)
	// 获取表单数据
	defer writeJSON(c, res)
	email := c.PostForm("email")
	phone := c.PostForm("telephone")
	password := c.PostForm("password")

	user := &model.User{
		Email:     email,
		Password:  password,
		Telephone: phone,
	}
	if len(user.Email) == 0 || len(user.Password) == 0 {
		// 另一种json格式的返回
		res["message"] = "email or password cannot be null"
		return
	}
	user.Password = utils.Md5(user.Password + user.Email)
	err = service.UserInsert(user)
	if err != nil {
		res["message"] = "email already exists"
		return
	}
	res["succeed"] = true
}

// 登录
func SigninPost(c *gin.Context) {
	var (
		err  error
		user *model.User
	)
	username := c.PostForm("username")
	password := c.PostForm("password")
	if username == "" || password == "" {
		c.HTML(http.StatusOK, "auth/signin.html", gin.H{
			"message": "username or password cannot be null",
		})
		return
	}
	user, err = service.GetUserByUsername(username)
	if err != nil || user.Password != utils.Md5(password+username) {
		c.HTML(http.StatusOK, "auth/signin.html", gin.H{
			"message": "密码或者用户名错误",
		})
		return
	}
	if user.LockState {
		c.HTML(http.StatusOK, "auth/signin.html", gin.H{
			"message": "你的账号已经被锁定",
		})
		return
	}
	//	 获取session
	session := sessions.Default(c)
	session.Clear()
	session.Set(SESSION_KEY, user.ID)
	session.Save()
	// 判断是否是管理员
	// mysql 中0表示false 1表示true
	if user.IsAdmin {
		c.Redirect(http.StatusMovedPermanently, "/admin/index")
	} else {
		c.Redirect(http.StatusMovedPermanently, "/")
	}
}

// 认证授权
func Oauth2Callback(c *gin.Context) {
	var (
		userInfo *GitHubUserInfo
		user     *model.User
	)
	code := c.Query("code")
	state := c.Query("state")

	//	 验证状态
	session := sessions.Default(c)
	if len(state) == 0 || state != session.Get(SESSION_GITHUB_STATE) {
		//	 终止会话
		c.Abort()
		return
	}
	//	 从会话状态删除
	session.Delete(SESSION_GITHUB_STATE)
	session.Save()

	//	 交换accesstoken
	token, err := exchangeTokenByCode(code)
	if err != nil {
		seelog.Error(err)
		c.Redirect(http.StatusMovedPermanently, "/signin")
		return
	}
	// 通过accesstoken获取github userinfo
	userInfo, err = getGithubUserInfoByAccessToken(token)

	if err != nil {
		seelog.Error(err)
		c.Redirect(http.StatusMovedPermanently, "/signin")
		return
	}
	sessionUser, exists := c.Get(CONTEXT_USER_KEY)

	if exists { // 已登录
		user, _ = sessionUser.(*model.User)
		_, err1 := service.GetIsGithubIdExists(userInfo.Login, user.ID)
		if err1 != nil { // 未绑定
			if user.IsAdmin {
				user.GithubLoginId = userInfo.Login
			}
			user.AvatarUrl = userInfo.AvatarUrl
			user.GithubUrl = userInfo.HTMLURL
			err = service.UpdateGithubUserInfo(user)
		} else {
			err = errors.New("this github loginId has bound another account.")
		}
	} else {
		user = &model.User{
			GithubLoginId: userInfo.Login,
			AvatarUrl:     userInfo.AvatarUrl,
			GithubUrl:     userInfo.HTMLURL,
		}
		if user.AvatarUrl == "" {
			user.AvatarUrl = "http://47.97.116.9:8080/static/libs/AdminLTE/img/user2-160x160.jpg"
		}
		user, err = service.GetFirstOrCreate(user)
		if err == nil {
			if user.LockState {
				err = errors.New("Your account have been locked.")
				HandleMessage(c, "Your account have been locked.")
				return
			}
		}
	}
	if err == nil {
		session := sessions.Default(c)
		session.Clear()
		session.Set(SESSION_KEY, user.ID)
		session.Save()
		if user.IsAdmin {
			c.Redirect(http.StatusMovedPermanently, "admin/index")
		} else {
			c.Redirect(http.StatusMovedPermanently, "/")
		}
		return
	}
}

// 交换accesstoken
func exchangeTokenByCode(code string) (accessToken string, err error) {
	var (
		transport *oauth.Transport
		token     *oauth.Token
	)
	transport = &oauth.Transport{
		Config: &oauth.Config{
			ClientId:     system.GetConfiguration().GithubClientId,
			ClientSecret: system.GetConfiguration().GithubClientSecret,
			RedirectURL:  system.GetConfiguration().GithubRedirectURL,
			TokenURL:     system.GetConfiguration().GithubTokenUrl,
			Scope:        system.GetConfiguration().GithubScope,
		}}
	token, err = transport.Exchange(code)
	if err != nil {
		return
	}
	accessToken = token.AccessToken
	// 缓存的令牌
	cacheFile := oauth.CacheFile("./request.token")
	if err := cacheFile.PutToken(token); err != nil {
		seelog.Error(err)
	}
	return
}

// 通过accesstoken获取github userinfo
func getGithubUserInfoByAccessToken(token string) (*GitHubUserInfo, error) {
	var (
		resp *http.Response
		body []byte
		err  error
	)
	resp, err = http.Get(fmt.Sprintf("https://api.github.com/user?access_token=%s", token))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	var userInfo GitHubUserInfo
	// 数据输出json
	err = json.Unmarshal(body, &userInfo)
	return &userInfo, err
}
func writeJSON(ctx *gin.Context, h gin.H) {
	if _, ok := h["succeed"]; !ok {
		h["succeed"] = false
	}
	ctx.JSON(http.StatusOK, h)
}

// 用户首页
func UserIndex(c *gin.Context) {
	users, _ := service.ListUsers()
	user, _ := c.Get(CONTEXT_USER_KEY)
	c.HTML(http.StatusOK, "admin/user.html", gin.H{
		"users":    users,
		"user":     user,
		"comments": service.MustListUnreadComment(),
	})
}

// 用户锁
func UserLock(c *gin.Context) {
	var (
		err  error
		_id  uint64
		res  = gin.H{}
		user *model.User
	)
	defer writeJSON(c, res)
	id := c.Param("id")
	// 将字符串解析为整数
	_id, err = strconv.ParseUint(id, 10, 64)
	if err != nil {
		res["message"] = err.Error()
		return
	}
	user, err = service.GetUser(uint(_id))
	if err != nil {
		res["message"] = err.Error()
		return
	}
	user.LockState = !user.LockState
	err = service.Lock(user)
	if err != nil {
		res["message"] = err.Error()
		return
	}
	res["succeed"] = true
}

// 获取用户信息以及评论
func ProfileGet(c *gin.Context) {
	sessionUser, exists := c.Get(CONTEXT_USER_KEY)
	if exists {
		c.HTML(http.StatusOK, "admin/profile.html", gin.H{
			"user":     sessionUser,
			"comments": service.MustListUnreadComment(),
		})
	}
}

// 修改信息
func ProfileUpdate(c *gin.Context) {
	var (
		err error
		res = gin.H{}
	)
	defer writeJSON(c, res)
	avatarUrl := c.PostForm("avatarUrl")
	nickName := c.PostForm("nickName")
	users, exists := c.Get(CONTEXT_USER_KEY)
	if !exists {
		res["message"] = "没有获取到用户信息"
		return
	}
	user := new(model.User)
	// 接口类型转换为实体类型
	user, ok := users.(*model.User)
	if !ok {
		res["message"] = "用户信息转换失败"
		return
	}
	err = service.UpdateProfile(user, avatarUrl, nickName)
	if err != nil {
		res["message"] = "修改信息失败:" + err.Error()
		return
	}
	res["succeed"] = true
	res["user"] = model.User{AvatarUrl: avatarUrl, NickName: nickName}
}

// 绑定邮箱
func BindEmail(c *gin.Context) {
	var (
		err error
		res = gin.H{}
	)
	defer writeJSON(c, res)
	email := c.PostForm("email")
	sessionUser, exists := c.Get(CONTEXT_USER_KEY)
	if !exists {
		res["message"] = "没有获取到用户信息"
		return
	}
	user, ok := sessionUser.(*model.User)
	if !ok {
		res["message"] = "用户信息转换失败"
		return
	}
	if len(user.Email) > 0 {
		res["message"] = "请传入邮箱"
		return
	}
	_, err = service.GetUserByUsername(email)
	if err != nil {
		res["message"] = "用户没有邮箱信息"
		return
	}
	err = service.UpdateUserEmail(user, email)

	if err != nil {
		res["message"] = err.Error()
		return
	}
	res["succeed"] = true
}

// 解除邮箱绑定
func UnbinEmail(c *gin.Context) {
	var (
		err error
		res = gin.H{}
	)
	defer writeJSON(c, res)
	sessionUser, exists := c.Get(CONTEXT_USER_KEY)
	if !exists {
		res["message"] = "没有获取到用户信息"
		return
	}
	user, ok := sessionUser.(*model.User)
	if !ok {
		res["message"] = "用户转换失败"
		return
	}
	if user.Email == "" {
		res["message"] = "没有邮箱信息"
		return
	}
	err = service.UpdateUserEmail(user, "")
	if err != nil {
		res["message"] = err.Error()
		return
	}
	res["succeed"] = true

}

// 解绑github
func UnbindGithub(c *gin.Context) {
	var (
		err error
		res = gin.H{}
	)
	defer writeJSON(c, res)
	sessionUser, _ := c.Get(CONTEXT_USER_KEY)
	user, ok := sessionUser.(*model.User)
	if !ok {
		res["message"] = "用户格式错误"
		return
	}
	if user.GithubLoginId == "" {
		res["message"] = "没有把绑定github"
		return
	}
	user.GithubLoginId = ""
	err = service.UpdateGithubUserInfo(user)
	if err != nil {
		res["message"] = "解绑失败：" + err.Error()
		return
	}
	res["succeed"] = true
}
